package com.rc.saas.payment.unwxpay.util;

import com.alibaba.fastjson.util.IOUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

/**
 * AES 加密算法实现
 * @ClassName: AESUtil
 * @Description: TODO
 * @author: GuangMing
 * @date: 2017年11月3日 下午12:29:06
 */
public class AESUtil {

    private static Cipher cipher;

    static {
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 加密
     * 
     * @Title: encrypt
     * @Description: TODO
     * @param strKey 加密秘钥
     * @param strIn 加密内容
     * @return
     * @throws Exception
     */
    public static synchronized String encrypt(byte[] strKey, byte[] strIn) throws Exception {
        initModel(strKey);
        byte[] encrypted = cipher.doFinal(strIn);
        return Base64.encode(encrypted);
    }

    /**
     * 加密
     * 
     * @Title: encrypt
     * @Description: TODO
     * @param strKey 加密秘钥
     * @param strIn 加密内容
     * @return
     * @throws Exception
     */
    public static synchronized String encrypt(byte[] strKey, String strIn) throws Exception {
        initModel(strKey);
        byte[] encrypted = cipher.doFinal(strIn.getBytes());
        return Base64.encode(encrypted);
    }

    private static void initModel(byte[] strKey) throws Exception, InvalidKeyException,
                                                 InvalidAlgorithmParameterException {
        SecretKeySpec skeySpec = getKey(strKey);
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);// , iv
    }

    /**
     * 加密
     * 
     * @Title: encrypt
     * @Description: TODO
     * @param strKey 加密秘钥
     * @param strIn 加密内容
     * @return
     * @throws Exception
     */
    public static synchronized String encrypt(byte[] strKey, String strIn, String charSet) throws Exception {
        initModel(strKey);
        byte[] encrypted = cipher.doFinal(strIn.getBytes(charSet));
        return Base64.encode(encrypted);
    }

    /**
     * AES解密
     * 
     * @Title: decrypt
     * @Description: TODO
     * @param strKey 解密秘钥
     * @param strIn 解密内容
     * @param charSet 字符集
     * @return
     * @throws Exception
     */
    public static synchronized String decrypt(byte[] strKey, String strIn, String charSet) throws Exception {
        SecretKeySpec skeySpec = getKey(strKey);
        cipher.init(Cipher.DECRYPT_MODE, skeySpec); // , iv
        byte[] encrypted1 = Base64.decode(strIn);
        byte[] original = cipher.doFinal(encrypted1);
        String originalString = new String(original, charSet);
        return originalString;
    }

    public static synchronized byte[] decryptToByte(byte[] strKey, String strIn) throws Exception {
        SecretKeySpec skeySpec = getKey(strKey);
        cipher.init(Cipher.DECRYPT_MODE, skeySpec); // , iv
        byte[] encrypted1 = Base64.decode(strIn);
        byte[] original = cipher.doFinal(encrypted1);
        return original;
    }

    public static byte[] buildeKey() {
        return SHA256Util.getSha256(String.valueOf(System.currentTimeMillis())).getBytes();
    }

    public static String buildeKeyStr() {
        return SHA256Util.getSha256(String.valueOf(System.currentTimeMillis()));
    }

    /**
     * AES 解密
     * 
     * @Title: decrypt
     * @Description: TODO
     * @param strKey 秘钥
     * @param strIn 字符集
     * @return
     * @throws Exception
     */
    public static synchronized String decrypt(byte[] strKey, String strIn) throws Exception {
        return decrypt(strKey, strIn, "UTF8");
    }

    private static SecretKeySpec getKey(byte[] strKey) throws Exception {
        byte[] arrBTmp = strKey;
        byte[] arrB = new byte[32]; // 创建一个空的16位字节数组（默认值为0）
        for (int i = 0; i < arrB.length; i++) {
            arrB[i] = arrBTmp[i];
        }
        SecretKeySpec skeySpec = new SecretKeySpec(arrB, "AES");
        return skeySpec;
    }

    public static AESItem getMD5KeyInstance(String key) throws Exception {
        return getMD5KeyInstance(key, "AES");
    }

    public static AESItem getMD5KeyInstance(String key, String type) throws Exception {
        return getMD5KeyInstance(key, type, "BC");
    }

    public static AESItem getMD5KeyInstance(String key, String type, String provider) throws Exception {
        return new AESItem(MD5Util.getMd5Byte(key), type, provider);
    }

    public static AESItem getAppendZEROInstance(byte[] keyBytes, String type, String provider) throws Exception {
        int keySize = keyBytes.length;
        byte[] keyBuffer = new byte[keySize <= 16 ? 16 : 32];
        if (keySize >= keyBuffer.length) {
            System.arraycopy(keyBytes, 0, keyBuffer, 0, keyBuffer.length);
        } else {
            System.arraycopy(keyBytes, 0, keyBuffer, 0, keySize);
            for (int i = keySize; i < keyBuffer.length; i++) {
                keyBuffer[i] = 0;
            }
        }
        return new AESItem(keyBuffer, type, provider);
    }

    public static AESItem getAppendZEROInstance(byte[] keyBytes, String type) throws Exception {
        return getAppendZEROInstance(keyBytes, type, "BC");
    }

    public static AESItem getAppendZEROInstance(byte[] keyBytes) throws Exception {
        return getAppendZEROInstance(keyBytes, "AES");
    }

    public static AESItem getAppendZEROInstance(String keyBytes, String type, String provider) throws Exception {
        return getAppendZEROInstance(keyBytes.getBytes("UTF-8"), type, provider);
    }

    public static AESItem getAppendZEROInstance(String keyBytes, String type) throws Exception {
        return getAppendZEROInstance(keyBytes.getBytes("UTF-8"), type, "BC");
    }

    public static AESItem getAppendZEROInstance(String keyBytes) throws Exception {
        return getAppendZEROInstance(keyBytes.getBytes("UTF-8"), "AES");
    }

    public static void writeByteArray(byte[] arrays) {
        System.out.println("byte Array{length:" + arrays.length + "}");
        for (int i = 0; i < arrays.length; i++) {
            System.out.print(arrays[i] + " ");
        }
        System.out.println();
    }

    
    public static final int FORMAT_BASE64 	= 0;
    public static final int FORMAT_BYTE	= 1;
    public static final int FORMAT_HEX 	= 2;
        
    public static void decrypt(File input,File output,String key,int FORMAT_TYPE) throws Exception{
    	AESItem aesItem =  AESUtil.getMD5KeyInstance(key, "AES/ECB/PKCS7Padding");
    	BufferedReader bufferedReader = null;
    	OutputStream outputStream = null;
    	InputStream inputStream = null;
    	byte[]buffer = new byte[4096+16];
    	byte[]datasByte = null;
    	String datas;
    	try{
    		outputStream = new FileOutputStream(output);
    		switch (FORMAT_TYPE) {
    		case FORMAT_BASE64:
    		case FORMAT_HEX:
    			bufferedReader = new BufferedReader(new FileReader(input));
    			while((datas = bufferedReader.readLine())!=null){
    				if(datas!=null){
    					if(FORMAT_HEX == FORMAT_TYPE){
    						datasByte = CodingUtil.hexStringToBytes(datas);
    					}else if(FORMAT_BASE64 == FORMAT_TYPE){
    						datasByte = CodingUtil.base64Decode(datas);
    					}
    					outputStream.write(aesItem.decrypt(datasByte));
    					outputStream.flush();
    				}
    			}
    			break;
			case FORMAT_BYTE:
				inputStream = new FileInputStream(input);
				byte[]encBuffer;
				int position = -1;
				while((position = inputStream.read(buffer)) > 0){
					encBuffer = new byte[position];
					System.arraycopy(buffer, 0, encBuffer, 0, position);
					encBuffer = aesItem.decrypt(encBuffer);
					outputStream.write(encBuffer);
					outputStream.flush();
				}
    			break;
    		}
    		
    	}catch(Exception e){
    		e.printStackTrace();
    	}finally{
    		
    		IOUtils.close(inputStream);
    		IOUtils.close(bufferedReader);
    		IOUtils.close(outputStream);
    	}
    }

    public static void encrypt(File input,File output,String key,int FORMAT_TYPE) throws Exception{
    	AESItem aesItem =  AESUtil.getMD5KeyInstance(key, "AES/ECB/PKCS7Padding");
		byte[]buffer = new byte[4096];
		byte[]encBuffer;
		InputStream inputStream = null;
		OutputStream outputStream = null;
		FileWriter fileWriter = null;
		String strBuffer;
		int postion = -1;
		try {
			inputStream = new FileInputStream(input);
			outputStream = new FileOutputStream(output);
			fileWriter = new FileWriter(output);
			while((postion = inputStream.read(buffer))> 0){
				encBuffer = new byte[postion];
				System.arraycopy(buffer, 0, encBuffer, 0, postion);
				encBuffer = aesItem.encrypt(encBuffer);
				
				switch (FORMAT_TYPE) {
				case FORMAT_BASE64:
					strBuffer = CodingUtil.base64Encode(encBuffer);
					fileWriter.write(strBuffer+"\n");
					fileWriter.flush();
					break;
				case FORMAT_HEX:
					strBuffer = CodingUtil.bytesToHexString(encBuffer);
					fileWriter.write(strBuffer+"\n");
					fileWriter.flush();
					break;
				case FORMAT_BYTE:
					outputStream.write(encBuffer);
					outputStream.flush();
					break;
				}
			}
			
		} catch (Throwable e) {
			e.printStackTrace();
		} finally{
			
			IOUtils.close(fileWriter);
			IOUtils.close(outputStream);
			IOUtils.close(inputStream);
		}
		
    }
    
    public static byte[] encrypt(String key, byte[] bytes) throws Exception{
    	AESItem aesItem =  AESUtil.getMD5KeyInstance(key, "AES/ECB/PKCS7Padding");
		byte[] encBuffer = aesItem.encrypt(bytes);
		return encBuffer;		
    }
    
// --------------------------------------------------------------------
    /*
	 * 转为十六进制
	 */
	private static String asHex(byte buf[]) {
		StringBuffer strbuf = new StringBuffer(buf.length * 2);
		int i;
		for (i = 0; i < buf.length; i++) {
			if (((int) buf[i] & 0xff) < 0x10)
				strbuf.append("0");
			strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
		}
		return strbuf.toString();
	}

	/*
	 * 转为二进制
	 */
	private static byte[] asBin(String src) {
		if (src.length() < 1)
			return null;
		byte[] encrypted = new byte[src.length() / 2];
		for (int i = 0; i < src.length() / 2; i++) {
			int high = Integer.parseInt(src.substring(i * 2, i * 2 + 1), 16);
			int low = Integer.parseInt(src.substring(i * 2 + 1, i * 2 + 2), 16);
			encrypted[i] = (byte) (high * 16 + low);
		}
		return encrypted;
	}

	/*
	 * 加密
	 */
	public static String encrypt(String data, String secret_key) {
		byte[] key = asBin(secret_key);
		SecretKeySpec sKey = new SecretKeySpec(key, "AES");
		try {
			Cipher cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.ENCRYPT_MODE, sKey);
			byte[] encrypted = cipher.doFinal(data.getBytes("utf-8"));
			return asHex(encrypted);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/*
	 * 解密
	 */
	public static String decrypt(String encData, String secret_key) {
		byte[] tmp = asBin(encData);
		byte[] key = asBin(secret_key);
		SecretKeySpec sKey = new SecretKeySpec(key, "AES");
		try {
			Cipher cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.DECRYPT_MODE, sKey);
			byte[] decrypted = cipher.doFinal(tmp);
			return new String(decrypted, "utf-8");
		} catch (Exception e) {
			return null;
		}
	}

	public static String getAutoCreateAESKey() {
		KeyGenerator kg = null;
		try {
			kg = KeyGenerator.getInstance("AES");
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		kg.init(128);// 要生成多少位，只需要修改这里即可128, 192或256
		SecretKey sk = kg.generateKey();
		byte[] b = sk.getEncoded();
		return asHex(b);
	}

}
